/**
 * Copyright (C) 2013 SHANGHAI VOLKSWAGEN, All rights reserved.
 * License version 1.0, a copy of which has been included with this.
 *
 * @File name：com.bwie.community.websocket.WebSocketServer
 * @Create on：2024/4/5
 * @Author：liuchang
 */
package com.bwie.community.websocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.domain.ChatHistory;
import com.bwie.common.domain.request.MessageVo;
import com.bwie.community.service.WebAdd;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>ClassName：WebSocketServer</p >
 * <p>Description：</p >
 * <p>Author：liuchang</p >
 * <p>Date：2024/4/5</p >
 */
@Component
@ServerEndpoint("/websocket/message/{room}/{name}")
public class WebSocketServer {
  private static final Logger LOGGER= LoggerFactory.getLogger(WebSocketServer.class);

  private static int socketMaxOnlineCount=100;

  private static Semaphore socketSemaphore=new Semaphore(socketMaxOnlineCount);

    /**
     * 静态变量，用来记录当前在线连接数，线程安全的类。
     */
    private static final AtomicInteger onlineSessionClientCount = new AtomicInteger(0);

    /*
     * 存放所有在线的客户端
     * */
    private static final Map<String,Map<String, Session>> onlineSessionClientMap =new ConcurrentHashMap<>();

    private String room;
    private String name;

    private static WebAdd webAdd;

    @Autowired
    private void setWebAdd(WebAdd webAdd){
         WebSocketServer.webAdd=webAdd;
    }

   /*
   *连接成功调用的方法
   * */
  public void onOpen(@PathParam("room") String room, @PathParam("name") String name, Session session){
      this.room = room;
      this.name = name;

      Map<String,Session> roomClients=onlineSessionClientMap.getOrDefault(room,new ConcurrentHashMap<>());

      //检查是否同名
      if (roomClients.containsKey(name)){
          onlineSessionClientCount.decrementAndGet();
          LOGGER.info("连接已存在，关闭之前的连接 ==> room = {}， name = {}。", room, name);


        //通知之前的连接被挤掉
         sendToOne(room, name, "您的账号在其他地方登录，您被迫下线。");

          roomClients.remove(name);

          // 将新连接加入房间中
          roomClients.put(name, session);

          // 在线数加1
          onlineSessionClientCount.incrementAndGet();

          sendToOne(room, name, "连接成功");

          LOGGER.info("连接建立成功，当前在线数为：{} ==> 开始监听新连接：room = {}， name = {}。", onlineSessionClientCount, room, name);
      }
  }

   /*
   *连接关闭时处理
   * */
    @OnClose
    public void onClose(@PathParam("room") String room, @PathParam("name") String name, Session session){
        // 获取该房间的客户端会话Map
        Map<String, Session> roomClients = onlineSessionClientMap.get(room);

        if (roomClients != null){
          roomClients.remove(name);
        }

        // 在线数减1
        onlineSessionClientCount.decrementAndGet();
        LOGGER.info("连接关闭成功，当前在线数为：{} ==> 关闭该连接信息：room = {}， name = {}。", onlineSessionClientCount, room, name);
    }

    /**
     * 抛出异常时处理
     */
    @OnError
    public void onError(Session session, Throwable exception) throws Exception
    {
        if (session.isOpen())
        {
            // 关闭连接
            session.close();
        }
        String sessionId = session.getId();
        LOGGER.info("\n 连接异常 - {}", sessionId);
        LOGGER.info("\n 异常信息 - {}", exception);
        // 移出用户
        WebSocketUsers.remove(sessionId);
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 服务器接收到客户端消息时调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        ChatHistory chatHistory = new ChatHistory();
        JSONObject jsonObject = JSON.parseObject(message);
        String fromUser = jsonObject.getString("from");
        String  toname=jsonObject.getString("to");
        String msg = jsonObject.getString("message");

        LOGGER.info("服务端收到客户端消息 ==> fromname = {}, toname = {}, message = {}", name, toname, message);


        /**
         * 模拟约定：如果未指定toname信息，则群发给房间内的所有人，否则就单独发送给指定人
         */
        if (toname == null || toname.isEmpty()) {
            sendToRoom(room, name, msg);
        } else {
            sendToOne(room, toname, msg);
        }
        chatHistory.setRoomId(room);
        chatHistory.setFromName(name);
        chatHistory.setName(toname);
        chatHistory.setCreateTime(new Date());
        chatHistory.setMessage(msg);
        webAdd.addLog(chatHistory);
    }


    private void sendToOne(String room, String toName, String message) {

        // 获取该房间的客户端会话Map
        Map<String, Session> roomClients = onlineSessionClientMap.get(room);

        if (roomClients != null) {
            // 通过toName查询对应的客户端会话
            Session toSession = roomClients.get(toName);

            if (toSession != null) {
                LOGGER.info("服务端给客户端发送消息 ==> room = {}, fromname = {}, toname = {}, message = {}", room, name, toName, message);
                chatHistory(room, name, message, toName, toSession);
            } else {
                LOGGER.error("服务端给客户端发送消息 ==> room = {}, toname = {} 不存在, message = {}", room, toName, message);
            }
        }
    }


   
    private void sendToRoom(String room, String from, String message) {
        // 获取该房间的客户端会话Map
        Map<String, Session> roomClients = onlineSessionClientMap.get(room);

        if (roomClients != null) {
            // 遍历房间内所有客户端会话
            roomClients.forEach((name, toSession) -> {
                // 排除掉发送者自己
                if (!name.equalsIgnoreCase(from)) {
                    LOGGER.info("服务端给客户端群发消息 ==> room = {}, fromname = {}, toname = {}, message = {}", room, from, name, message);
                    chatHistory(room, from, message, name, toSession);
                }
            });
        }
    }

    private void chatHistory(String room, String from, String message, String name, Session toSession) {
        ChatHistory chatHistory = new ChatHistory();
        chatHistory.setRoomId(room);
        chatHistory.setFromName(from);
        chatHistory.setName(name);
        chatHistory.setCreateTime(new Date());
        chatHistory.setMessage(message);

        toSession.getAsyncRemote().sendText(JSON.toJSONString(chatHistory));
    }


}
